package com.dgl.sdk.MultiDownloader;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;

import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 下载器
 * @author Tibib
 *
 */
public class MultiFileDownloader {

	protected static final String TAG = "FileDownloader";
	protected Context context;
	protected FileService fileService;

	/* 已下载文件长度 */
	protected long downloadSize = 0;

	/* 原始文件长度 */
	protected long fileSize = 0;

	/* 线程数 */
	protected DownloadThread[] threads;

	/* 本地保存文件 */
	protected File saveFile;

	/* 缓存各线程下载的长度*/
	protected Map<Integer, Long> data = new ConcurrentHashMap<Integer, Long>();

	/* 每条线程下载的长度 */
	protected long block;

	/* 下载路径  */
	protected String downloadUrl;

//	/* 下载是否完成Handler */
//	private Handler finishHandler;

	/* 文件保存路径 */
	protected String fileSaveDir;

	/* 文件名称 */
	protected String fileName;

	/* 开启下载的线程数 */
	protected int threadNum;

	/* 下载是否暂停 */
	protected boolean isRun = true;
	private HttpURLConnection conn;
    private long time;

    //下载回调
    public interface DownloadCallback{
        void onStart();
        void onProgress(int progress);
        void onComplete(String filePath);
        void onError(String error);
    }

    private DownloadCallback downloadCallback;

    private Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                //下载进度
                case 0:
                    downloadCallback.onProgress(getDownloadPercent());
                    break;
                //下载完成
                case 1:
                    downloadCallback.onComplete((String)msg.obj);
                    break;
                //下载失败
                case 2:
                    downloadCallback.onError((String)msg.obj);
                    break;
                default:
                    break;
            }
        }
    };

	/**
	 * 构建文件下载器
	 */

	public MultiFileDownloader(Context context, String downloadUrl, String fileSaveDir, String fileName,
                               int threadNum, DownloadCallback downloadCallback) {
		
		this.context = context;

        this.downloadUrl = downloadUrl;
        this.fileSaveDir = fileSaveDir;
        this.fileName = fileName;
        this.threadNum = threadNum;
        this.downloadCallback = downloadCallback;
	}
	
	public void reDownLoad(){
		URL url = null;
		try {
			url = new URL(this.downloadUrl);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		for (int i = 0; i < this.threads.length; i++) {//开启线程进行下载
			long downLength = this.data.get(i+1);
			
			if(downLength < this.block && this.downloadSize<this.fileSize){//判断线程是否已经完成下载,否则继续下载	
				this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);
				this.threads[i].start();
				//Thread.sleep(5*1000);
			}else{
				this.threads[i] = null;
			}
		}
	}
	
	/**
	 * 开始下载
	 * @throws Exception
	 */
	public void download(){

        //用新开的线程去下载，不然conn.connect(); 会失败， 不知何故!!
        new Thread(){

            @Override
            public void run() {
                super.run();

                try {
                    startDownload();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }.start();


	}
	
	private void startDownload() throws Exception {
        //初始化数据
        try{
            initData();
        }catch(Exception e){
            throw e;
        }

        try {
            RandomAccessFile randOut = new RandomAccessFile(MultiFileDownloader.this.saveFile, "rw");
            if(MultiFileDownloader.this.fileSize>0) randOut.setLength(MultiFileDownloader.this.fileSize);
            randOut.close();
            URL url = new URL(MultiFileDownloader.this.downloadUrl);

            if(MultiFileDownloader.this.data.size() != MultiFileDownloader.this.threads.length){
                MultiFileDownloader.this.data.clear();

                for (int i = 0; i < MultiFileDownloader.this.threads.length; i++) {
                    MultiFileDownloader.this.data.put(i+1, 0L);//初始化每条线程已经下载的数据长度为0
                }
            }

            for (int i = 0; i < MultiFileDownloader.this.threads.length; i++) {//开启线程进行下载
                long downLength = MultiFileDownloader.this.data.get(i+1);

                if(downLength < MultiFileDownloader.this.block && MultiFileDownloader.this.downloadSize<MultiFileDownloader.this.fileSize){//判断线程是否已经完成下载,否则继续下载
                    MultiFileDownloader.this.threads[i] = new DownloadThread(MultiFileDownloader.this, url, MultiFileDownloader.this.saveFile,
                            MultiFileDownloader.this.block, MultiFileDownloader.this.data.get(i+1), i+1);
                    MultiFileDownloader.this.threads[i].start();
                    //Thread.sleep(5*1000);
                }else{
                    MultiFileDownloader.this.threads[i] = null;
                }
            }


        } catch (Exception e) {
            print(e.toString());
            throw new Exception("file download fail");
        }
    }
	/**
	 * 下载之前进行数据的初始化工作
	 * @throws Exception
	 */
	private void initData() throws Exception{
		try {
			
			//为暂停后重新下载做准备
			this.downloadSize = 0L;
			this.isRun = true;
			
			this.fileService = new FileService(this.context);
			URL url = new URL(this.downloadUrl);
			this.threads = new DownloadThread[this.threadNum];
			
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(30*1000);
			conn.setRequestMethod("GET");
			conn.setRequestProperty("Accept", "video/*, image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
			conn.setRequestProperty("Accept-Language", "zh-CN");
			conn.setRequestProperty("Referer", downloadUrl); 
			conn.setRequestProperty("Charset", "UTF-8");
			conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
			conn.setRequestProperty("Connection", "Keep-Alive");
			conn.connect();
			printResponseHeader(conn);
			
			if (conn.getResponseCode()==200) {
				this.fileSize = conn.getContentLength();//根据响应获取文件大小
				if (this.fileSize <= 0) throw new RuntimeException("Unkown file size ");
				
				//没有指定文件名
				if(this.fileName!=null&&!"".equals(this.fileName)){
					this.saveFile = new File(this.fileSaveDir + this.fileName + ".dltmp");//构建保存文件
				}else{//否则获取服务器的文件名称
					String filename = getFileName(conn);//获取文件名称
					this.saveFile = new File(this.fileSaveDir+filename+ ".dltmp");//构建保存文件
				}
				
				Map<Integer, Long> logdata = this.fileService.getData(this.downloadUrl);//获取下载记录
				
				Log.i(TAG, "数据库存在的线程下载数据:"+logdata);
				
				if(logdata.size()>0){//如果存在下载记录
					for(Map.Entry<Integer, Long> entry : logdata.entrySet())
						this.data.put(entry.getKey(), entry.getValue());//把各条线程已经下载的数据长度放入data中
				}
				
				if(this.data.size()==this.threads.length){//下面计算所有线程已经下载的数据长度
					for (int i = 0; i < this.threads.length; i++) {
						this.downloadSize += this.data.get(i+1);
					}
					
					print("已经下载的长度"+ this.downloadSize);
				}
				
				//计算每条线程下载的数据长度
				this.block = (this.fileSize % this.threads.length)==0? this.fileSize / this.threads.length : this.fileSize / this.threads.length + 1;
			}else{
				throw new Exception("server no response ");
			}
		} catch (Exception e) {
			print(e.toString());
			throw new Exception("don't connection this url");
		}
	}
	
	/**
	 * 获取文件名
	 * @param conn
	 * @return
	 */
	protected String getFileName(HttpURLConnection conn) {
		String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1);
		
		if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称
			for (int i = 0;; i++) {
				String mine = conn.getHeaderField(i);
				
				if (mine == null) break;
				
				if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){
					Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase());
					if(m.find()) return m.group(1);
				}
			}
			
			filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名
		}
		
		return filename;
	}
	
	public boolean isRun() {
		return isRun;
	}

	public void setRun(boolean isRun) {
		this.isRun = isRun;
	}
	
	public void stop(){
		conn.disconnect();
	}
	/**
	 * 当前下载的长度
	 * @return
	 */
	public long getDownloadSize() {
		return downloadSize;
	}

	/**
	 * 获取线程数
	 */
	public int getThreadSize() {
		return threads.length;
	}
	
	/**
	 * 获取文件大小
	 * @return
	 */
	public long getFileSize() {
		return fileSize;
	}
	
	
	
//	public Handler getFinishHandler() {
//		return finishHandler;
//	}

	
	
	public Map<Integer, Long> getData() {
		return data;
	}

	public FileService getFileService() {
		return fileService;
	}

	public void setDownloadSize(long downloadSize) {
		this.downloadSize = downloadSize;
	}

	public void delete(){
		if(this.fileService == null){
			this.fileService = new FileService(this.context);
		}
		this.fileService.delete(this.downloadUrl);

        File file = new File(fileSaveDir, fileName+ ".dltmp");
        if(file.exists()){
            file.delete();
        }
	}
	/**
	 * 累计已下载大小
	 * @param size
	 */
	protected synchronized void append(int size) {

        downloadSize += size;
        if(downloadSize>=this.fileSize){//下载完成
            //清楚数据库表数据
            this.fileService.delete(this.downloadUrl);

            changeFileName(this.saveFile);

            String filePath = this.fileSaveDir +"/" + this.fileName;

            Message msg = new Message();
            msg.what = 1;
            msg.obj = filePath;

            handler.sendMessage(msg);
        }else{

            //间隔500毫秒才发送一次更新
            long thisTime = SystemClock.uptimeMillis();
            if(thisTime - time >= 500){
                time = thisTime ;

                Message msg = new Message();
                msg.what = 0;
                handler.sendMessage(msg);
            }

        }
		

	}
	
	private void changeFileName(File saveFile) {

		String oldname = saveFile.getAbsolutePath();
		String newname = oldname.replace(".dltmp", "");

		if (!oldname.equals(newname)) {
			File newFile = new File(newname);
			if (newFile.exists()) {
				System.out.println("您没有对文件名做修改，不能执行~");
			} else {
				saveFile.renameTo(newFile);
			}
		}
	}

	/**
	 * 更新指定线程最后下载的位置
	 * @param threadId 线程id
	 * @param pos 最后下载的位置
	 */
	public synchronized void update(int threadId, long pos) {
		this.data.put(threadId, pos);
		this.fileService.update(this.downloadUrl, this.data);
	}
	
	/**
	 * 获取下载的百分比
	 * @return 百分比
	 */
	public int getDownloadPercent(){
		
		return (int)(downloadSize*100/fileSize);
		
	}
	
	/**
	 * 获取Http响应头字段
	 * @param http
	 * @return
	 */
	public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) {
		Map<String, String> header = new LinkedHashMap<String, String>();
		
		for (int i = 0;; i++) {
			String mine = http.getHeaderField(i);
			if (mine == null) break;
			header.put(http.getHeaderFieldKey(i), mine);
		}
		
		return header;
	}
	
	/**
	 * 打印Http头字段
	 * @param http
	 */
	public static void printResponseHeader(HttpURLConnection http){
		Map<String, String> header = getHttpResponseHeader(http);
		
		for(Map.Entry<String, String> entry : header.entrySet()){
			String key = entry.getKey()!=null ? entry.getKey()+ ":" : "";
			print(key+ entry.getValue());
		}
	}

	/**
	 * 打印日志信息
	 * @param msg
	 */
	protected static void print(String msg){
		Log.i(TAG, msg);
	}

    public Handler getHandler(){
        return handler;
    }
}
